package utils

import (
	"fmt"
	"github.com/golang-jwt/jwt/v4"
	"sync"
	"time"
)

/** jwt-go 操作工具*/

// HMAC(对称加密) 定义密钥
var jwtHmacKey = []byte("my-siging-key")

// 结果体
type JwtUtil struct {
}

// 单例
var (
	jwtUtilInstance *JwtUtil
	jwtUtilOnce     sync.Once
)

// GetJWTUtil 获取JWT工具单例（使用默认配置）
func GetJwtUtil() *JwtUtil {
	jwtUtilOnce.Do(func() {
		jwtUtilInstance = &JwtUtil{}
	})
	return jwtUtilInstance
}

//// 定义new函数来构建结构体 -被单例和GetJwtUtil替换
//func NewJwtUtil() *JwtUtil {
//	return &JwtUtil{}
//}

/**
 * ValidateToken 验证并解析Token
 * tokenString: JWT Token字符串
 * claims: 用于接收解析结果的声明结构体指针
 */
func (this *JwtUtil) ValidateToken(tokenStr string, claims jwt.Claims) error {
	token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
		// 验证签名
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
		}
		return jwtHmacKey, nil
	})

	if err != nil {
		return err
	}

	if !token.Valid {
		return fmt.Errorf("invalid token")
	}

	return nil
}

/**
 * ValidateAndGetClaims 验证Token并返回解析后的声明
 * tokenString: JWT Token字符串
 * 返回解析后的声明结构体
 */
func (this *JwtUtil) ValidateAndGetClaims(tokenString string) (jwt.Claims, error) {
	// 先使用BaseClaims尝试解析
	claims := &BaseClaims{}
	err := this.ValidateToken(tokenString, claims)
	if err != nil {
		return nil, err
	}
	return claims, nil
}

/**
 * CreateTokenWithDefaultClaims 创建带默认注册声明的Token
 * customClaims: 任意自定义结构体
 * issuer: 签发者
 * subject: 主题
 * expiresIn: 过期时间（秒）
 */
func (this *JwtUtil) CreateTokenWithClaims(
	customClaims interface{},
	issuer, subject string,
	expiresIn time.Duration) (string, error) {
	// 创建基础声明
	claims := BaseClaims{
		CustomClaims: customClaims,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(expiresIn)),
			IssuedAt:  jwt.NewNumericDate(time.Now()),
			NotBefore: jwt.NewNumericDate(time.Now()),
			Issuer:    issuer,
			Subject:   subject,
		},
	}

	return this.createToken(claims)
}

/**
 * CreateToken 创建JWT Token，支持任意结构体作为声明
 * claims: 自定义声明结构体，必须包含jwt.RegisteredClaims或实现jwt.Claims接口
 * expiresIn: 过期时间（秒）
 */
func (this *JwtUtil) createToken(claims jwt.Claims) (string, error) {

	// 创建token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 签名并获取完整token
	return token.SignedString(jwtHmacKey)
}

// BaseClaims 基础声明结构体，包含自定义声明和标准声明
type BaseClaims struct {
	CustomClaims interface{} `json:"info,omitempty"`
	jwt.RegisteredClaims
}

func test() {
	/* //使用方式：
	_userClaim := &UserClaims{
		123,
		"丽丽",
	}

	jwtUtilCtx := utils.GetJwtUtil()
	token, err := jwtUtilCtx.CreateTokenWithClaims(_userClaim, "my-app", "user-auth", 24*time.Hour)
	if err != nil {
		fmt.Println("token创建失败:", err)
		return
	}
	fmt.Printf("生成的用户JWT Token: %s\n", token)

	var parseUserClaim utils.BaseClaims
	if err := jwtUtilCtx.ValidateToken(token, &parseUserClaim); err != nil {
		fmt.Println("验证token失败：", err)
	}
	fmt.Println("解析token: ", parseUserClaim)
	*/
}
